﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Diagnostics;

namespace FileSyncConsole
{
    class Program
    {
        const string config = "config.xml";
        const string index = "FileSyncIndex.xml";
        static void Main(string[] args)
        {
            //先检测自己目录下面是否有一个config.xml文件，如果没有，则创建一个空白的，并且退出程序

            if (args.Length > 0)
            {
                //如果有参数，则进行特定的操作
                //-R表示重置某个目录的同步状态，-S表示来源目录，-D目标目录，也就是说将来源目录视为已经同步过了。在所有的子目录下面都生成那个index文件

                if (args[0] == "-R")
                {
                    string source = args[1].Substring(2);
                    string dest = args[2].Substring(2);

                    ResetFolderStatus(source, dest);
                    Console.WriteLine("目录重置完成");
                    Console.Read();
                }

                return;
            }

            //默认没有参数的话，就执行同步
            if (!File.Exists(config))
            {
                CreateConfigurationFile();
                Console.Read();
            }
            else
            {

                Stopwatch watch = new Stopwatch();
                watch.Start();
                //读取配置文件
                XDocument doc = XDocument.Load(config);
                var mapping = from i in doc.Element("Items").Elements("Item")
                              select new
                              {
                                  Source = i.Element("SourceDirctory").Value,
                                  Dest = i.Element("DestDirctory").Value,
                                  ExcludeExts = i.Element("ExcludeExts").Value,
                                  Overwrite = bool.Parse(i.Element("Overwrite").Value)
                              };

                foreach (var item in mapping)
                {
                    CopyFolder(item.Source, item.Dest, item.ExcludeExts, item.Overwrite);
                }

                watch.Stop();
                Console.WriteLine("====同步完成,处理文件数:{0},耗费时间:{1}秒====", fileCount,watch.ElapsedMilliseconds/1000);

            }
            Console.Read();
        }


        static void ResetFolderStatus(string source, string dest) {
            //如果文件夹下面有FileSyncIndex这个文件，则删除掉

            string indexFile = Path.Combine(source, index);
            if (File.Exists(indexFile))
                File.Delete(indexFile);

            XDocument doc = new XDocument(
                    new XDeclaration("1.0", "UTF-8", "yes"),
                    new XComment("这是文件同步工具的索引文件，"
                        + "记录了当前目录中文件同步的状态。" +
                        "关于该工具的反馈，可以发邮件到ares@xizhang.com"),
                    new XElement("Files"));

            XElement root = doc.Element("Files");

            DirectoryInfo dir = new DirectoryInfo(source);

            var files = from f in dir.GetFiles()
                        where f.Name != index
                        select new
                        {
                            FileName = f.Name,
                            LastModify = f.LastWriteTime.Ticks
                        };

            foreach (var item in files)
            {
                root.Add(new XElement("File",
                     new XAttribute("FileName", item.FileName),
                     new XAttribute("Target", dest),
                     new XAttribute("LastModify",item.LastModify.ToString())));
            }

            Console.WriteLine("重置:{0}", source);
            doc.Save(indexFile);

            foreach (DirectoryInfo subdir in dir.GetDirectories())
            {
                ResetFolderStatus(subdir.FullName, Path.Combine(dest, subdir.Name));
            }
        }
        

        static int fileCount = 1;
        static void CopyFolder(string source, string dest, string exclude, bool overwrite)
        {
            //遍历本文件夹中的所有文件，复制过去
            //如果有子目录，则也复制过去

            DirectoryInfo dir = new DirectoryInfo(source);

            

            if (!Directory.Exists(dest))
                Directory.CreateDirectory(dest);//如果目录不存在，还要创建该目录

            List<string> exts = new List<string>(exclude.Split(','));

            //在每个来源目录下面检查一个特殊的文件，
            //这个文件固定名称为FileSyncIndex.xml.如果没有发现该文件，则创建一个新的

            XDocument doc;
            string indexFile = Path.Combine(source, index);
            if (File.Exists(indexFile))
                doc = XDocument.Load(indexFile);
            else
                doc = new XDocument(
                    new XDeclaration("1.0", "UTF-8", "yes"),
                    new XComment("这是文件同步工具的索引文件，"
                        +"记录了当前目录中文件同步的状态。"+
                        "关于该工具的反馈，可以发邮件到ares@xizhang.com"),
                    new XElement("Files"));

            XElement root=doc.Element("Files");


            Console.WriteLine("扫描目录:{0}", source);

            var currentStatus = from s in root.Elements("File")
                                select new
                                {
                                    FileName = s.Attribute("FileName").Value,
                                    Target = s.Attribute("Target").Value,
                                    LastModify = long.Parse(s.Attribute("LastModify").Value)
                                };


            var files = from f in dir.GetFiles()
                        where !exts.Contains(f.Extension) && f.Name != index
                        select f;


            var query = from f1 in files
                        let temp = currentStatus.FirstOrDefault(f => f.FileName == f1.Name && f.Target == dest)
                        where temp == null || f1.LastWriteTime.Ticks > temp.LastModify
                        select f1;

            string timestamp = DateTime.Now.ToString("yyyyMMddhhmmss");
            foreach (FileInfo file in query)
            {
                try
                {
                    if (!exts.Contains(file.Extension))
                    {
                        string destFile = Path.Combine(dest, file.Name);

                        //如果选项是要覆盖
                        if (overwrite)
                        {
                            if (File.Exists(destFile))
                            {
                                FileInfo f = new FileInfo(destFile);
                                if (f.IsReadOnly)
                                    f.IsReadOnly = false;
                            }
                            file.CopyTo(destFile, overwrite);
                        }
                        else//建立多版本
                        {
                            //如果该文件已经存在，则修改它的名字
                            if (File.Exists(destFile))
                            {
                                string newname = Path.Combine(dest, string.Format("{0}-({1}存档版本){2}",
                                    Path.GetFileNameWithoutExtension(file.Name),
                                    timestamp,
                                    file.Extension));
                                File.Move(destFile, newname);//这里并不是真的移动文件，因为在同一个目录，所以其实是重命名
                            }

                            file.CopyTo(Path.Combine(dest, destFile));
                        }


                        //更新index文件
                        XElement e = root.Elements("Files").FirstOrDefault(
                            x =>
                                x.Attribute("FileName").Value == file.Name &&
                                x.Attribute("Target").Value == dest
                                );


                        if (e != null)
                        {
                            //更新
                            e.Attribute("LastModify").Value = file.LastWriteTime.Ticks.ToString();
                        }
                        else
                        {//添加新元素
                            root.Add(new XElement("File",
                                new XAttribute("FileName", file.Name),
                                new XAttribute("Target", dest),
                                new XAttribute("LastModify", file.LastWriteTime.Ticks.ToString())));

                        }


                        Console.WriteLine("{0}\t同步:{1}成功",fileCount++,file.FullName);

                    }
                }
                catch 
                {
                    continue;
                    //Console.WriteLine("{2}\t同步:{0}出错:{1}", file.FullName, ex.Message,fileCount++);

                }
            }

            doc.Save(indexFile);


            foreach (DirectoryInfo subdir in dir.GetDirectories())
            {
                CopyFolder(subdir.FullName, Path.Combine(dest, subdir.Name), exclude, overwrite);
            }

        }

        static void CreateConfigurationFile() {
            XDocument doc = new XDocument(
                new XDeclaration("1.0", "UTF-8", "yes"),
                new XComment("这是文件同步工具的配置文件,请参考如下范例，插入Item节点"),
                new XElement("Items",
                    new XElement("Item",
                        new XElement("DestDirctory", @"H:\Test",
                            new XComment("这是目标目录的设置")),
                        new XElement("SourceDirctory", @"E:\Temp\Test",
                            new XComment("这是来源目录的设置")),
                        new XElement("ExcludeExts", ".tmp,.temp",
                            new XComment("这是要排除的文件类型,用逗号分开多个扩展名")),
                        new XElement("Overwrite", false,new XComment("是否要覆盖？如果为true，则如有新版本，则直接覆盖目标文件，反之，则创建多版本。")))));

            doc.Save(config);

            Console.WriteLine("当前还没有完成配置，请检查config.xml文件");
        }

    }
}
